﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

using alib.Enumerable;
using alib.Debugging;

namespace agree
{
#if false
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// for transforming sequences of TDL tokens into persisted TFSs.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public partial class TfsBuilder3 : TdlParsePos
	{
		public TfsBuilder3(Type t)
			: base(t)
		{
		}

		public override String ToString()
		{
			try
			{
				String crf = corefs == null ? "(null)" : corefs.Count.ToString();
				return String.Format("{0} corefs: {1}", base.ToString(), crf);
			}
			catch (Exception ex)
			{
				return ex.ToString();
			}
		}
		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		Dictionary<String, Coreference> corefs;

		[DebuggerDisplay("{ToString(),nq}")]
		class Coreference : List<TmFeatMark>, IAddRangeList<TmFeatMark>
		{
			public Coreference(String s_tag)
				: base(10)
			{
				this.s_tag = s_tag;
			}
			public String s_tag;

			public override String ToString()
			{
				return String.Format("{0}  {1}  {2}",
					s_tag,
					this.StringJoin(" "));
			}
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void AddCoref(String tag, TypeMgr _tm, FeatMark fm)
		{
			Coreference ent;
			if (corefs == null)
				corefs = new Dictionary<String, Coreference>();
			else if (corefs.TryGetValue(tag, out ent))
				goto ok;
			corefs.Add(tag, ent = new Coreference(tag));
		ok:
			TmFeatMark tfm = new TmFeatMark(_tm, fm);
			//Edge e = constraint(fm);
			//Debug.Assert(!e.IsCoreferenced);
			//e = CreateEdge(_tm, e.FlagsId | Edge.Flag.Coreference, e.Mark);
			//tfm.SetConstraint(this, e);
			ent.Add(tfm);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// entry point
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static TfsBuilder3 FromTdl(TypeMgr tm, IEnumerable<IList<TdlTok>> rgbfc, Type t = null)
		{
			Debug.Assert(rgbfc != null);

			var e = rgbfc.GetEnumerator();
			while (e.MoveNext())
			{
				Type tcur = fcm.feat_arr[tm.GetFeatureIndex(e.Current[0].i_s)].introduced_by;
				if (tcur == t || tcur == null)
					continue;
				Debug.Assert(!tcur.IsTop);
				if (t == null)
					t = tcur;
				else if ((t = t.tm.UnifyTypesFull(t, tcur)) == null)
					throw new TdlException("inferred types for topmost features do not unify.");
			}
			Debug.Assert(t != null && t.HasAppropriateFeatures);

			TfsBuilder3 bn = new TfsBuilder3(t);
			bn._accept_multiple_base_constraints(rgbfc);
			return bn;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static TfsBuilder3 FromXdlRels(IList<TransferRule.XdlRel> rels, out TransferTfs.RelModeEdge[] rgrme)
		{
			if (rels.Count == 0)
				throw new Exception();
			TfsBuilder3 bn = new TfsBuilder3(null);
			rgrme = bn._load_xdl(rels);
			return bn;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		TransferTfs.RelModeEdge[] _load_xdl(IList<TransferRule.XdlRel> rels)
		{
			var rgrme = new TransferTfs.RelModeEdge[rels.Count];
			TransferRule.XdlRel rel = null;
			for (int i = 0; i < rels.Count; i++)
			{
				rel = rels[i];
				Debug.Print(rel.ToString());
				rgrme[i].trm = rel.trm;
				rgrme[i].e = _do_one_xdl_rel(rel);
			}
			Debug.Print("---------------");

			//if (corefs != null)
			//    corefs._fixup_corefs(rel.tg);

			return rgrme;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge _do_one_xdl_rel(TransferRule.XdlRel rel)
		{
			/// this is a bit hacky: set the TypeMgr for this iteration, so that it will not only be used
			/// for the tdl parse, but so that it gets captured into the TmFeatMarks that keep track of coreferencing
			base.tm = rel.tm;

			var tdlt = new TdlTokenizer(rel.reldef.s_rel);
			var tok_grps = new TdlConstraintGrouper(IdentifierTokenGroup.Type.TransferRule, tdlt);
			ConsDef consdef = tok_grps.ConstraintBody().First().constraints;

			Type t = tm.TopType;
			foreach (IList<TdlTok> toks in consdef)
			{
				Type tcur = fcm.feat_arr[tm.GetFeatureIndex(toks[0].i_s)].introduced_by;
				if (tcur == t || tcur == null)
					continue;
				Debug.Assert(!tcur.IsTop);
				if (t == null)
					t = tcur;
				else if ((t = t.tm.UnifyTypesFull(t, tcur)) == null)
					throw new TdlException("inferred types for root features in rel do not unify.");
			}
			Debug.Assert(t != null && t.HasAppropriateFeatures);

			Edge e = CreateEdge(t, false);
			foreach (IList<TdlTok> toks in consdef)
				_accept_base_feature_constraint(e, toks);
			return e;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void _accept_multiple_base_constraints(IEnumerable<IList<TdlTok>> rg_toks)
		{
#if true
			var d = base.d;
			Debug.Assert(d.Count == 0);
			Debug.Assert(base.next_mark == 2);
			foreach (IList<TdlTok> toks in rg_toks)
			{
				_accept_base_feature_constraint(TopEdge, toks);

				//if (corefs != null)
				//{
				//    if (corefs.Values.Any(L => !L.IsDistinct()))
				//        Nop.X();
				//    this.corefs = null;
				//}

				d.Clear();
				base.reverse = null;
				base.next_mark = 2;
			}
#endif

			//foreach (IList<TdlTok> toks in rg_toks)
			//    _accept_base_feature_constraint(TopEdge, toks);

			//if (corefs != null)
			//{
			//    corefs._fixup_corefs(null);
			//    corefs = null;
			//}
		}

		List<arr_tfs_entry> late = new List<arr_tfs_entry>();

		const TdlTok.Type TypeStopToks = TdlTok.Type.Identifier | TdlTok.Type.Tag | TdlTok.Type.XMapTag | TdlTok.Type.String |
										TdlTok.Type.SquareOpen | TdlTok.Type.AngleOpen | TdlTok.Type.DifferenceListOpen;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _accept_base_feature_constraint(Edge e, IList<TdlTok> toks)
		{
			base.Reset(toks);

			if (base.MoveNext())
				AcceptFeatures(0);

			if (!base.Eof)
				throw new TdlException(base.Current, "TDL parser returned without using all tokens.");
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge AcceptFeatures(TdlTok.Type stop_toks)
		{
			int m_dst = next_mark++;
			//Debug.Assert(m_dst > 0);
			Edge.Flag nf = 0;
			while (true)
			{
				//  _                 _
				// |  current          |
				// |  FEAT  type & ... |
				// |_ ^               _|

				VerifyTokenType(TdlTok.Type.Identifier, "Expected a feature name (1)");
				int i_feat = tm.GetFeatureIndex(base.CurString);
				if (i_feat == -1)
					throw new Exception();

				Type tt_maximal = tm.GetMaximalTypeForFeature(i_feat);
				if ((nf = tm.UnifyTypesFull(nf, tt_maximal.EdgeFlag)) < 0)
					throw new Exception();

				TdlTok.Type tok = MoveNextThrow().t;

				Edge e;
				if (tok == TdlTok.Type.Dot)
				{
					//  _               _
					// | current         |
					// | F . G type ...  |
					// |_      ^        _|

					MoveNextThrow();

					e = AcceptFeatures(stop_toks | TdlTok.Type.Comma);

					if ((TdlTok.Type.Comma & base.CurType) != 0)
					{
						late.Add(new arr_tfs_entry(i_feat, m_dst, e));
						break;
					}
					Nop.X();

				}
				else if ((tok & TypeStopToks) != 0)
				{
					//  _                 _
					// | current           |
					// | FEAT  type &  ... |
					// |_      ^          _|
					//
					e = AcceptType(i_feat, stop_toks | TdlTok.Type.Comma);
					late.Add(new arr_tfs_entry(i_feat, m_dst, e));
				}
				else
					throw new TdlException(Current, "Expected: '.', type identifier, tag, '[', '<', or '<!'.");

				if (base.Eof)
					break;
				else if ((stop_toks & base.CurType) != 0)
					break;
				else
					throw new TdlException(Current, "Unexpected end of token stream while parsing TDL.");
			}
			if ((nf & Edge.Flag.EtmNonBareType) == 0)
				m_dst = 0;
			return new Edge(nf, m_dst);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  _                                        _
		// | current                                  |
		// | FEAT    type & #tag & [constraint] & ... |
		// |_        ^                               _|
		//
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge AcceptType(int __ifeat, TdlTok.Type stop_toks)
		{
			s_feat = fcm.feat_arr[__ifeat].feature.ToUpper();


			Edge.Flag ef_cur = 0;
			int m_cur = next_mark++;
			List<FeatMark> tags;

			while (!base.Eof)
			{
				TdlTok.Type ttok = base.CurType;

				if (ttok == TdlTok.Type.Identifier)
				{
					Ident(ref ef_cur);
					MoveNext();
				}
				else if (ttok == TdlTok.Type.String)
				{
					str(ref ef_cur);
					MoveNext();
				}
				else if (ttok == TdlTok.Type.Tag)
				{
					tag(ref tags);
					MoveNext();
				}
				else if (ttok == TdlTok.Type.SquareOpen)
				{
					MoveNextThrow();
					Edge.Flag nf = Sq();

					MoveNextThrow();
				}
				else if (ttok == TdlTok.Type.AngleOpen)
				{
					Angle(ref ef_cur);
				}
				else if (ttok == TdlTok.Type.DifferenceListOpen)
				{
					Dlist(ref ef_cur);
				}
				else
					throw new TdlException(Current, "Expected a type, tag, or constraint specification");


				// type group [ x ]  < y , >  <! z , !>  < x . y > originating from this base level...
				if (Eof || ((ttok = base.CurType) & stop_toks) != 0)
				{
					if ((ef_cur & Edge.Flag.EtmNonBareType) == 0)
						m_cur = 0;
					return tm.CreateEdge(ef_cur, m_cur, ref next_mark);
				}

				// additional constraints on this type.
				if (ttok != TdlTok.Type.Ampersand)
					throw new TdlException(Current, "Expected '&'");
				MoveNext();
			}
			throw new TdlException(Current, "Unexpected end of token stream while parsing TDL.");
		}


		void Ident(ref Edge.Flag ef_cur)
		{
			Type tt;
			if ((tt = tm.LookupType(Current)).IsTop)
			{
				/// not calling 'UnifyInConstraint' means that no edge is added to the dictionary. If the condition
				/// persists, a EmptyFeatureStructureTfs will be eventually created instead
			}
			else
			{
				UpdateType(ref ef_cur, tt.EdgeFlag, err2);
			}
		}

		void str(ref Edge.Flag ef_cur)
		{
			String es = tm.GetStringValue(ef_cur);
			if (es != null && String.Compare(es, Current.i_s) != 0)
			{
				Exception(0, ef_cur, err3, Current.i_s);
			}
			else
			{
				/* must unify down to string */
				if (!tm.CanUnifyToString(ef_cur))
					Exception(0, 0, err4);

				// 'RNAME', if it's the only constraint, can also result in a TFS with edge count 0
				//if (__ifeat != rtc.f_ix_rname)
				//    ef_cur = tm.CreateStringEdge(Current.i_s).FlagsId;
			}
		}


		void tag(ref List<String> tags)
		{
			if (tags == null)
				tags = new List<string>();
			tags.Add(Current.i_s);
		}

		Edge Sq()
		{
			Edge e = default(Edge);
			if (base.CurType == TdlTok.Type.SquareClose)
			{
				//   < [ ] , ... >
				//       ^
				//	(do nothing)

			}
			else
			{
				e = AcceptFeatures(TdlTok.Type.SquareClose);

				//late.Add(new arr_tfs_entry(i_feat, m_cur, e));
			}
			return e;
		}

		void Angle(ref Edge.Flag ef_cur)
		{
			//  _                            _
			// | current                      |
			// | FEAT    < >                  |
			// |           ^                  |
			// | FEAT    < ... >              |
			// |           ^                  |
			// | FEAT    < type ...  >        |
			// |           ^                  |
			// | FEAT    < #tag ... >         |
			// |           ^                  |
			// | FEAT    < [ constraint ... > |
			// |_          ^                 _|

			int c_parts = Current.c_parts;
			MoveNextThrow();

			Edge e_list = AcceptList(c_parts);

			UpdateType(ref ef_cur, e_list.FlagsId, err6);

			VerifyTokenType(TdlTok.Type.AngleClose);
		}

		void Dlist(ref Edge.Flag ef_cur)
		{
			int c_parts = Current.c_parts;
			MoveNextThrow();

			int m = next_mark++;

			if ((ef_cur = tm.UnifyTypesFull(ef_cur, tm.tt_dlist.EdgeFlag)) < 0)
				throw new TdlException(Current, "Error expanding difference list. Existing type {0} for feature {1} failed to unify with difference list type {2} ",
					tm.TypeNameOrStringValue(ef_cur), s_feat, tm.tt_dlist.Name);

			dl1(m, c_parts);

			VerifyTokenType(TdlTok.Type.DifferenceListClose);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void dl1(int m_dst, int c_parts)
		{
			String last_coref = "diff-list-" + Guid.NewGuid().ToString();

			//FeatMark fm_list = ;

			//if (c_parts > 0)
			AcceptDifferenceList(m_dst, c_parts, last_coref);
			//else
			//{
			//Edge e_list = tm.CreateEdge(tm.tt_list, false, ref next_mark);
			//late.Add(new arr_tfs_entry(fm_list, e_list));

			//AddCoref(last_coref, tm, fm_list);

			//if (unify_in_constraint_type(ref cref_dl, tm.tt_list) < 0)
			//    throw new TdlException(Current, "Error expanding difference list. Existing type {0} for feature {1} failed to unify with list type {2} ",
			//        constraint_type(cref_dl).Name,
			//        s_feat,
			//        tm.tt_list.Name);

			//}
			// unify 'LAST list'

			FeatMark fm_last = new FeatMark(rtc.f_ix_dlist_last, m_dst);


			Edge e_last = tm.CreateEdge(tm.tt_list, false, ref next_mark);
			late.Add(new arr_tfs_entry(fm_last, e_last));

			AddCoref(last_coref, tm, fm_last);

			//if (unify_in_constraint_type(ref cref_dl, tm.tt_list) < 0)
			//    throw new TdlException(Current, "Error expanding difference list. Existing type {0} for feature {1} failed to unify with list type {2} ",
			//        tm.TypeNameOrStringValue(ef_cur),
			//        s_feat,
			//        tm.tt_list.Name);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge AcceptDifferenceList(int m_dst, int c_parts, String last_coref)
		{
			//String s_feat = fcm.feat_arr[fm.i_feat].feature.ToUpper();

			//if (unify_in_constraint_type(ref cr, tm.tt_ne_list) < 0)
			//    throw new TdlException(Current, "Error expanding difference list. Existing type {0} for feature {1} failed to unify with list type {2} ", constraint_type(cr).Name, s_feat, tm.tt_ne_list.Name);

			int m_next = next_mark++;

			//FeatMark fm_list = new FeatMark(rtc.f_ix_dlist_list, m_dst);
			var eee = c_parts > 0 ? tm.tt_ne_list : tm.tt_list;
			Edge e_list = tm.CreateEdge(c_parts > 0 ? tm.tt_ne_list : tm.tt_list, false, ref next_mark);
			//Edge e_list = new Edge(eee.EdgeFlag, m_next);
			late.Add(new arr_tfs_entry(rtc.f_ix_dlist_list, m_dst, e_list));

			//if (c_parts == 0)
			//    return;

			if (c_parts-- > 0)
			{
				Edge e_head = AcceptType(rtc.f_ix_list_head, TdlTok.Type.TokMap_DlsCl_Comma);

				// select feature 'REST'
				//switch_to_feature(ref cref_fwd, rtc.f_ix_list_tail);

				//if (unify_in_constraint_type(ref cref_fwd, tm.tt_list) < 0)
				//    throw new TdlException(Current, "Error expanding difference list. Existing type {0} for feature {1} failed to unify with list type {2} ", constraint_type(cref_fwd).Name, cref_fwd.Feature.ToUpper(), tm.tt_list.Name);
				//FeatMark fm_tail = new FeatMark(rtc.f_ix_list_tail, e_list.Mark);
				Edge e_last = tm.CreateEdge(tm.tt_list, false, ref next_mark);
				//Edge e_last = new Edge(tm.tt_list.EdgeFlag, m_next);
				late.Add(new arr_tfs_entry(rtc.f_ix_dlist_last, m_dst, e_last));

				if (Current.t == TdlTok.Type.Comma)
					MoveNextThrow();

				if (Current.t == TdlTok.Type.DifferenceListClose)
					Debug.Assert(c_parts == 0);

				if (c_parts > 0)
				{
					///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
					/// recurse down list parts
					///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

					AcceptDifferenceList(e_list.Mark, c_parts, last_coref);
				}

			}
			//else
			//{
			//    /AddCoref(last_coref, tm, fm_tail);
			//}

			return default(Edge);
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge AcceptList(int c_parts)
		{
			//        else if ((ef_cur = tm.UnifyTypesFull(ef_cur, tm.tt_empty.EdgeFlag)) < 0)
			//throw new TdlException(Current,,
			//    tm.TypeNameOrStringValue(ef_cur), s_feat, tm.tt_empty.Name);

			//FeatMark fm_head = new FeatMark(rtc.f_ix_list_head, m_dst);
			//FeatMark fm_tail = new FeatMark(rtc.f_ix_list_tail, m_dst);

			Edge.Flag ef_cur = tm.tt_list.EdgeFlag;

			if (c_parts-- > 0)
			{
				Edge e_head = AcceptType(rtc.f_ix_list_head, TdlTok.Type.TokMap_AngCl_Comma_Dot);

				Type term_type = tm.tt_list;
				if (Current.t == TdlTok.Type.Dot)
				{
					Debug.Assert(c_parts == 1, "should have prevented dotted pair with >2 parts during tokenization");
					//  _                            _
					// | current                      |
					// | FEAT    < a . b >            |
					// |_            ^               _|
					MoveNextThrow();
					Edge e_tail = AcceptType(rtc.f_ix_list_tail, TdlTok.Type.TokMap_AngCl);
					c_parts--;
				}
				else if (Current.t == TdlTok.Type.Comma)
				{
					MoveNextThrow();
					if (Current.t == TdlTok.Type.Ellipsis)
					{
						Debug.Assert(c_parts == 0);
						MoveNextThrow();
					}
				}
				else if (Current.t == TdlTok.Type.AngleClose)
				{
					Debug.Assert(c_parts == 0);
					term_type = tm.tt_empty;
				}

				//                if (
				//#if PET_ENFORCES_LIST_TERM_TYPE==true
				//                    term_type==tm.tt_empty &&
				//#endif
				//unify_in_constraint_type(ref cref_fwd, term_type) < 0)
				//                    throw new TdlException(Current, "Error expanding list. Existing type {0} for feature {1} failed to unify with list type {2} ", constraint_type(cref_fwd).Name, cref_fwd.Feature.ToUpper(), term_type.Name);

				///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
				/// recurse down list parts
				///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
				if (c_parts > 0)
				{
					//AcceptList(fm_tail.m, c_parts);

					///fm.SetConstraint...
					//set_constraint(cr, cref_fwd.Host);
				}
			}
			return default(Edge);
		}


		void Exception(String err, params Object[] args)
		{
			String s = err
				.Replace("{cur-feat}", s_feat)
				.Replace("{string-type}", tm.StringType.Name)
				.Replace("{cur-string}", Current.i_s);
			throw new TdlException(Current, s);
		}
		void Exception(Edge.Flag ef_prev, Edge.Flag ef_cur, String err, params Object[] args)
		{
			String s = err
				.Replace("{cur-type}", tm.TypeNameOrStringValue(ef_cur))
				.Replace("{prev-type}", tm.TypeNameOrStringValue(ef_prev))
				.Replace("{max-type}", "maximal type");
			Exception(s, args);
		}

		void UpdateType(ref Edge.Flag ef_cur, Edge.Flag nf, String err, params Object[] args)
		{
			var ef_prev = ef_cur;
			ef_cur = tm.UnifyTypesFull(ef_cur, nf);

			if (ef_cur < 0)
				Exception(ef_prev, ef_cur, err, args);
		}

		String s_feat;

		const string err1 = "Error expanding list. Existing type {prev-type} for feature {cur-feat} failed to unify with list type {cur-type}";
		const string err2 = "Existing type {prev-type} on feature {cur-feat} does not unify with {cur-type}";
		const string err3 = "String value '{prev-type}' for feature {cur-feat} does not unify with '{0}'";
		const string err4 = "Existing type {prev-type} on feature {cur-feat} failed to unify with string type '{string-type}'";
		const string err5 = "Error inferring type. Feature {cur-string} was not defined for any type";
		const string err6 = "Error expanding list. Existing type {prev-type} for feature {cur-feat} failed to unify with empty list type {0}";
		const string err7 = "Error following feature path specification. Expected a feature name after '.'";
		const string err8 = "Error inferring type. '{cur-string}' is not appropriate for  '{cur-type}'. (Maximal type '{max-type}' for feature '{cur-feat}' does not unify with previously seen type '{prev-type}'.)";
	};
#endif

}